package com.core.spring;

import com.core.comment.ConfigUtil;
import com.core.comment.StringUtil;
import com.core.netty.http.Intercept.Intercept;
import com.core.netty.http.work.MySerlvetUrl;
import com.core.spring.annotate.MyAuto;
import com.core.spring.annotate.MyBean;
import com.core.spring.annotate.MyConfig;
import com.core.spring.annotate.MyService;
import com.core.springaop.annotate.MyAscept;
import com.core.springmvc.MyController;
import com.core.springmvc.MyRequestMapping;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;


public class DefaultMyApplication implements MyApplication {
    //ioc核心容器
    private volatile Map<String, Object> classMap = new ConcurrentHashMap<>();
    //接口容器指向clssMap
    private volatile Map<String, String> interMap = new ConcurrentHashMap<>();
    //名称容器指向classMap
    private volatile Map<String, String> nameMap = new ConcurrentHashMap<>();

    //mvc相关的class
    private List<Class<?>> requestClassList = new LinkedList<>();
    //aop相关的class
    private List<Class<?>> aopClassList = new LinkedList<>();
    //所有拦截器的class
    private List<Class<?>> interceptClassList = new LinkedList<>();
    //所有serlvet的class
    private List<Class<?>> serlvetClassList = new LinkedList<>();
    //所有Config的class
    private Class<?> configClass = null;

    public DefaultMyApplication() {
        this(false);
    }

    public DefaultMyApplication(boolean isJar) {
        try {
            String scan = ConfigUtil.get("spring.scan");
            if (isJar) {
                initJar(scan);
            } else {
                init(scan);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Object getBean(String name) {

        return get(name, null);
    }

    public <T> T getBean(Class<?> clazz) {
        return (T) get(null, clazz);
    }

    public synchronized void put(String name, Class<?> clazz, Object value) {
        classMap.put(clazz.getName(), value);
        if (StringUtil.isNotBlack(name)) {
            nameMap.put(name, clazz.getName());
        }
        interMap.put(clazz.getName(), clazz.getName());
        for (Class c : clazz.getInterfaces()) {
            interMap.put(c.getName(), clazz.getName());
        }
    }

    /**
     * 获取容器内的实例并且给实例内注入相应的实例
     *
     * @param name
     * @param clazz
     * @return
     */
    public Object get(String name, Class<?> clazz) {
        Object re = null;
        if (StringUtil.isNotBlack(name)) {
            String na = nameMap.get(name);
            if (na != null) re = classMap.get(na);
            na = null;
        }
        if (re == null && clazz != null) {
            String na = interMap.get(clazz.getName());
            re = classMap.get(na);
            na = null;
        }
        try {
            // 注入实例到myauto标签的字段
            if (re != null) {
                Field[] fields = re.getClass().getDeclaredFields();
                for (Field field : fields) {
                    MyAuto myAuto = field.getAnnotation(MyAuto.class);
                    if (myAuto != null) {

                        field.setAccessible(true);
                        if (StringUtil.isNotBlack(myAuto.value())) {
                            field.set(re, this.get(myAuto.value(), null));
                        } else {
                            field.set(re, this.get(null, field.getType()));
                        }

                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return re;
    }

    /**
     * 初始化mybean注解下的所以类实例到内存中保存
     *
     * @param path
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private void init(String path) throws ClassNotFoundException, IllegalAccessException, InstantiationException {

        String rootpath = DefaultMyApplication.class.getClassLoader().getResource("").getPath();
        String p = rootpath + path.replaceAll("\\.", "/");
        //System.out.println("扫描根路径:" + p);
        File dir = new File(p);
        if (!dir.exists()) {
            throw new RuntimeException("请配置扫描包的根路径");
        }

        for (File file : dir.listFiles()) {
            if (file.isDirectory()) {
                init(path + "." + file.getName());
            } else {

                String className = path + "." + file.getName().replace(".class", "");

                Class<?> clazz = Class.forName(className);
                //处理名字
                BeanInfo beanInfo = doName(clazz);
                if (beanInfo.instance) {
                    Object o = clazz.newInstance();
                    this.put(beanInfo.name, clazz, o);
                }
                collectClass(clazz);

            }
        }

    }

    private void initJar(String path) throws ClassNotFoundException, IllegalAccessException, InstantiationException, IOException {

        ClassLoader classLoader = DefaultMyApplication.class.getClassLoader();
        String rootpath = classLoader.getResource("com").getPath();
        rootpath = rootpath.substring(0, rootpath.indexOf("com"));
        rootpath = rootpath.replaceAll("\\!", "");
        rootpath = rootpath.replaceAll("file:/", "");
        path = path.replaceAll("\\.", "/");
        System.out.println("扫描根路径:" + rootpath);

        JarFile jarFile = new JarFile(rootpath);
        Enumeration<JarEntry> jarEntries = jarFile.entries();
        while (jarEntries.hasMoreElements()) {
            JarEntry jarEntry = jarEntries.nextElement();
            String jarEntryName = jarEntry.getName();

            if (jarEntryName.contains(path)) {

                if (jarEntry.getName().endsWith(".class")) {
                    String className = jarEntry.getName().replace(".class", "");
                    className = className.replaceAll("/", ".");
                    Class<?> clazz = Class.forName(className);
                    //处理名字
                    BeanInfo beanInfo = doName(clazz);
                    if (beanInfo.instance) {
                        Object o = clazz.newInstance();
                        this.put(beanInfo.name, clazz, o);
                    }
                    collectClass(clazz);
                }
            }

        }


    }

    private void collectClass(Class<?> clazz) {
        //收集mvc相关的class
        if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
            requestClassList.add(clazz);
        }
        //收集aop相关的class
        if (clazz.isAnnotationPresent(MyAscept.class)) {
            aopClassList.add(clazz);
        }
        //收集拦截器的class
        if (clazz.isAnnotationPresent(Intercept.class)) {
            interceptClassList.add(clazz);
        }
        //收集serlvet的class
        if (clazz.isAnnotationPresent(MySerlvetUrl.class)) {
            serlvetClassList.add(clazz);
        }
        //收集config的class
        if (clazz.isAnnotationPresent(MyConfig.class)) {
            configClass = clazz;
        }
    }

    private BeanInfo doName(Class<?> clazz) {
        BeanInfo beanInfo = new BeanInfo();
        String name = null;
        MyBean myBean = clazz.getAnnotation(MyBean.class);
        MyController myController = clazz.getAnnotation(MyController.class);
        MyService myService = clazz.getAnnotation(MyService.class);
        if (name == null && myBean != null) name = myBean.value();
        if (name == null && myController != null) name = myController.value();
        if (name == null && myService != null) name = myService.value();
        beanInfo.name = name;
        beanInfo.instance = myBean != null || myController != null || myService != null;

        return beanInfo;

    }

    class BeanInfo {
        public String name;
        public boolean instance;
    }


    public List getRequestClassList() {

        return requestClassList;
    }

    public List getAopClassList() {
        return aopClassList;
    }

    public List getInterceptClassList() {
        return interceptClassList;
    }

    public List getSerlvetClassList() {
        return serlvetClassList;
    }

    public Class<?> getConfigClass() {
        return configClass;
    }
}
